package com.wawj.compplugin

import com.wawj.compplugin.utils.CmdUtil
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.bundling.Jar
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.NodeList

import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory

/**
 * 子工程的setting.gradle需要应用的插件
 * 从主仓库的线上分支读取components_configs.xml中的组件版本号，替换子工程中所有所有组件的版本号。
 * 给每个组件添加aar上传配置，以及处理组件aar上传关系的依赖。
 */
class SubRepoSettingsPlugin implements Plugin<Settings> {
    Settings settings
    File tempPointsFile
    SubRepoSettingsExtension extension

    @Override
    void apply(Settings settings) {
        this.settings = settings
        extension = settings.extensions.create("subrepo_settings", SubRepoSettingsExtension)
        settings.gradle.settingsEvaluated {
            println("----->>${settings.rootProject.name}子工程开始配置")
            loadComponentsConfigsXml()
            applyComponentsVersion()

        }
    }

    /**
     * 下载主仓库线上分支的components_configs.xml文件，
     * 下载错误，使用缓存，没有缓存报错提示。
     */
    void loadComponentsConfigsXml() {
        def gitToken = extension.gitToken
        def gitlabProjectId = extension.gitlabProjectId
        def releaseBranch = extension.releaseBranch
        def pointsFileRepoPath = extension.pointsFileRepoPath
        def encodeFilePath = URLEncoder.encode(pointsFileRepoPath, "UTF-8")
        if (gitToken == null || gitlabProjectId == null || releaseBranch == null || pointsFileRepoPath == null) {
            throw new RuntimeException("${settings.rootProject.name}的settings.gradle中缺少subrepo_settings插件的配置")
        }

        tempPointsFile = new File(settings.rootDir.absolutePath + File.separator + ".idea" + File.separator + "temp_components_points.xml")
        def gitUrl = "http://gitlab.it.5i5j.com/api/v4/projects/${gitlabProjectId}/repository/files/${encodeFilePath}/raw?ref=${releaseBranch}"
        def cmdCommand = "curl --connect-timeout 4 -m 4 -X GET -H 'PRIVATE-TOKEN:${gitToken}' '${gitUrl}'"
        String[] cmd = new String[]{"sh", "-c", cmdCommand}
        def result = CmdUtil.executeForOutput(cmd, null, false)

        def downloadOk = result.contains("<project version=")
        if (downloadOk) {
            //下载成功，更新文件
            tempPointsFile.withWriter('UTF-8') { it.write(result) }
            println("临时版本文件更新成功")
        } else if (!tempPointsFile.exists()) {
            //下载失败，没有缓存，直接报错停止运行
            throw new RuntimeException("components_points.xml下载失败，请检查网络连接\n" + result)
        } else {
            //下载失败，有缓存，使用缓存。
            println("临时版本文件更新失败，使用缓存")
        }
    }

    /**
     * 单独运行替换版本号
     */
    void applyComponentsVersion() {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
        DocumentBuilder builder = factory.newDocumentBuilder()
        FileInputStream pointsInputStream = new FileInputStream(tempPointsFile)
        Document pointsDoc = builder.parse(pointsInputStream)
        Element pointsRootElement = pointsDoc.getDocumentElement()
        NodeList compNodeList = pointsRootElement.getElementsByTagName("component")
        Element mavenElement = pointsRootElement.getElementsByTagName("maven").item(0)
        String aarGroup = mavenElement.getAttribute("aarGroup")

        settings.gradle.allprojects {
            switchCompAarVersion(it, compNodeList, aarGroup)
            applyMavenPublish(it, compNodeList, mavenElement, aarGroup)
        }
    }

    def switchCompAarVersion(Project project, compNodeList, aarGroup) {
        project.configurations.all {
            resolutionStrategy {
                dependencySubstitution { dependency ->
                    compNodeList.each {
                        String compName = it.getAttribute("name")
                        String aarVersion = it.getAttribute("aarVersion")
                        dependency.substitute(dependency.module("${aarGroup}:$compName")).using(dependency.module("${aarGroup}:$compName:$aarVersion"))
                    }
                }
            }
        }
    }

    def applyMavenPublish(Project project, compNodeList, mavenElement, aarGroup) {
        project.ext.publishAarVersion = ""//可以在build.gradle中重新为此扩展字段赋值
        project.plugins.apply("maven-publish")


        project.afterEvaluate {
            if (project.plugins.hasPlugin("com.android.library")) {
                def sourcesJar = project.tasks.create('sourcesJar', Jar) {
                    from project.android.sourceSets.main.java.srcDirs
                    classifier "sources"
                }
                project.android.libraryVariants.all { variant ->
                    if (variant.name == "release") {
                        project.publishing {
                            publications {
                                "${project.name}"(MavenPublication) {
                                    from project.components.release// buildTypes的名字
                                    groupId = aarGroup//组名
                                    artifactId = project.name// 当前项目的名字
                                    version = project.publishAarVersion = getAarVersion(project, project.publishAarVersion)
                                    artifact sourcesJar
                                }
                            }
                            repositories {
                                maven {
                                    // 根据libVersionName判断仓库地址
                                    url = getMavenUrl(project, project.publishAarVersion, mavenElement)
                                    // 仓库用户名密码
                                    credentials {
                                        username = mavenElement.getAttribute("user")
                                        password = mavenElement.getAttribute("password")
                                    }
                                }
                            }
                        }

                        //自定义task包装maven-publish的发布aar的task。
                        //需写在libraryVariants.all里面，否则找不到依赖的task......
                        project.tasks.create("publishAar") {
                            setGroup "aar"
                            dependsOn project.getTasksByName("publish${project.name.capitalize()}PublicationToMavenRepository", false)[0]
                            doLast {
                                println "--------------------------------------------"
                                println "${project.name} aar上传成功"
                                println "版本号：" + project.publishAarVersion
                                println "--------------------------------------------"
                            }
                        }

                        Element foundPointElement = compNodeList.find { ((Element) it).getAttribute("name") == project.name }
                        if (foundPointElement == null) return
                        foundPointElement.getElementsByTagName("dependency").each {
                            def depProjectName = ((Element) it).getAttribute("name")
                            project.getTasksByName("publishAar", false)[0].dependsOn project.project(":${depProjectName}").getTasksByName("publishAar", false)
                        }

                    }
                }
            }
        }
    }

    def getAarVersion(project, aarVersionName) {
        // 原则上不允许本地编译允许本地上传release aar，
        // 但修改此值为true后也允许上传release aar，修改后记得及时改为false。
        // 但也只是针对指定了版本号的情况(build.gradle的aarVersionName不能为空)。
        // 为了正式版的稳定，尽量不要使用本地上传正式包。
        def allowManualPublishReleaseAar = false
        def version
        if (project.hasProperty('jenkinsAarVersion')) {
            //如果jenkins传来了版本号
            version = project.property('jenkinsAarVersion')
        } else if (aarVersionName == null || aarVersionName.isBlank()) {
            //为空，自动生成版本号
            version = new Date().format("yy.MMdd.HHmm.ss") + "-SNAPSHOT"
        } else {
            //使用build.gradle中定义的版本号，并追加-SNAPSHOT后缀
            if (!allowManualPublishReleaseAar && !isSnapshot(aarVersionName)) {
                aarVersionName += "-SNAPSHOT"
            }
            version = aarVersionName
        }
        return version
    }

    def getMavenUrl(project, aarVersionName, Element mavenElement) {
        if (isSnapshot(getAarVersion(project, aarVersionName))) {
            return mavenElement.getAttribute("snapshotUrl")
        } else {
            return mavenElement.getAttribute("releaseUrl")
        }
    }

    def isSnapshot(String aarVersionName) {
        boolean snapshot = false
        if (aarVersionName.toLowerCase().contains('snapshot')
                || aarVersionName.toLowerCase().contains('debug')
                || aarVersionName.toLowerCase().endsWith('dev'))
            snapshot = true
        return snapshot
    }

}

class SubRepoSettingsExtension {
    String gitToken
    String gitlabProjectId
    String releaseBranch
    String pointsFileRepoPath
}
